<?php 
/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                     (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published 
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.
    
    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/
 
    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews"; 
    
    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,\"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)\" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/
?><?php
/**
 * Validation class.
 *
 * This file contaings the validation class for CakePHP.
 *
 * PHP versions 4 and 5
 *
 * @author      Evan Sagge <evansagge@gmail.com>
 * @package     evansagge
 * @subpackage  evansagge.cake.model.util
 */

/**
 * Validation class.
 *
 * This class contains methods for implementing advanced model validation in CakePHP.
 * Validation for a field is enabled through the {@link @AppModel::validate} variable.
 *
 * PHP versions 4 and 5
 *
 * @author      Evan Sagge <evansagge@gmail.com>
 * @package     evansagge
 * @subpackage  evansagge.cake
 * @see         AppModel
 */
class Validation
{
    /**
     * Reference for model data
     */
    var $data;

    /**
     * Reference for model object
     */
    var $model; 
   
    var $errorCount = 0;

    /**
     * Constructor for validation class.  This initializes the data to validate as well as the model
     * against which data should be validated.
     *
     * @param mixed $data The data to validated.
     * @param object $model The model object against which the data should be validated.
     * @return Validation
     */
    function Validation(&$data, &$model)
    {
        $this->data =& $data;
        $this->model =& $model;
        $this->name =& $this->model->name;
    }
   
    /**
     * Evaluates the given validation result.  If the value is set to true, it will return true;
     * otherwise, it has two options: if $params['message'] is defined, it will add its value to
     * the model object's $validationErrors array and return false, else it will add the value of
     * the concatenation of the humanized field name and the passed $messageOnFail string to the
     * model object's $validationErrors array and return false.
     *
     * @param bool $validation The validation result.
     * @param string $messageOnFail The default message to return if the validation results to
     *   false.
     * @param string $fieldName The field name.
     * @param array $params Extra validation parameters.
     * @return Validation
     */   
    function _evaluate($validation, $messageOnFail, $fieldName = null, $params = array())
    {
        if ($validation)
        {
            return true;
        }
       
        if (!isset($params['message']))
        {
            $params['message'] = Inflector::humanize($fieldName) . " " . $messageOnFail . ".";
        }
       
        if ($params['message'])
        {
            $this->model->validationErrors[$this->name][$fieldName] = $params['message'];
        }
       
        $this->errorCount++;
        return false;
    }

    /**
     * Checks if the value defined by the field name is not empty.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is not empty; false otherwise.
     */
    function validateNotEmpty($fieldName, $params)
    {
        return $this->_evaluate(!empty($this->data[$this->name][$fieldName]), "should not be empty",
                $fieldName, $params);
    }
   
    /**
     * Alias for Validation::validateNotEmpty()
     */
    function validateRequired($fieldName, $params)
    {
        return $this->_evaluate($this->validateNotEmpty($fieldName, $params), "is required",
                $fieldName, $params);
    }

    /**
     * Matches the value defined by the field name against the pattern specified by
     * $params['pattern'].
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Contains the pattern to match the value of the field name against.
     * @return bool True if pattern matches the value of the field name; false otherwise.
     */
    function validatePattern($fieldName, $params)
    {
        $pattern = $params['pattern'];
        return $this->_evaluate(preg_match($pattern, $this->data[$this->name][$fieldName]),
                "does not match pattern {$pattern}", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is a valid word, i.e. contains only
     * alphanumeric characters or the underscore ('_') character.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True value of the field name is a valid word; false otherwise.
     * @see Validation::validatePattern()
     */
    function validateWord($fieldName, $params)
    {
        $params['pattern'] = '/^\\w*$/';
        return $this->_evaluate(!$this->validatePattern($fieldName, $params),
                "is not a valid word", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is an integer.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True value of the field name is an integer; false otherwise.
     * @see Validation::validatePattern()
     */
    function validateInteger($fieldName, $params)
    {
        $params['pattern'] = '/^\\d+$/';
        return $this->_evaluate(!$this->validatePattern($fieldName, $params),
                "is not a valid integer", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is a number.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True value of the field name is a floating point number; false otherwise.
     * @see Validation::validatePattern()
     */
    function validateNumber($fieldName, $params)
    {       
        if (isset($params['integerOnly']))
        {
            $params['pattern'] = '/^\\d+$/';
        }
        else
        {
            $params['pattern'] = '/^(\\d+)|(\\d*\.\\d+)$/';
        }
        return $this->_evaluate(!$this->validatePattern($fieldName, $params),
                "is not a valid number", $fieldName, $params);
    }   

    /**
     * Checks if the value defined by the field name has a valid e-mail address format.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name has a valid e-mail address format; false
     *      otherwise.
     * @see Validation::validatePattern()
     */
    function validateEmail($fieldName, $params)
    {
        $params['pattern'] = '/\\A(?:^([a-z0-9][a-z0-9_\\-\\.\\+]*)@([a-z0-9]'
                . '[a-z0-9\\.\\-]{0,63}\\.(com|org|net|biz|info|name|net|pro|aero|coop|museum|'
                . '[a-z]{2,4}))$)\\z/i';
        return $this->_evaluate(!$this->validatePattern($fieldName, $params),
                "is not a valid email", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is a valid value for a year.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is a valid value for a year; false
     *      otherwise.
     * @see Validation::validatePattern()
     */
    function validateYear($fieldName)
    {
        $params['pattern'] = '/^[12][0-9]{3}$/';
        return $this->_evaluate(!$this->validatePattern($fieldName, $params),
                "is not a valid year value", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is unique for the given data model.  The
     *      check for uniqueness is case-insensitive.  If $params['conditions'] is given,
     *      this is used as a constraint.  If $params['scope'] is given, the value of
     *      the field name is only checked against records that match the value of the
     *      column/field defined by $params['scope'].
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is unique; false otherwise.
     * @see Model::hasAny()
     */
    function validateUnique($fieldName, $params)
    {
        $val = $this->data[$this->name][$fieldName];
        $column = $this->name . '.' . $fieldName;
        $id = $this->name . '.' . $this->model->primaryKey;

        $conditions = array();
        if (isset($params['conditions']))
        {
            $conditions = $params['conditions'];
        }

        if (isset($params['scope']))
        {
            if (is_array($params['scope']))
            {
                foreach ($params['scope'] as $scope)
                {
                    $conditions[$scope] = $this->data[$this->name][$scope];
                }
            }
            else if (is_string($params['scope']))
            {
                $conditions[$params['scope']] = $this->data[$this->name][$params['scope']];
            }
        }

        $conditions[$column] = $val;
        if (!empty($this->data[$this->name][$this->model->primaryKey]))
        {
            $conditions[$id] = ('!=' . $this->data[$this->name][$this->model->primaryKey]);
        }

        return $this->_evaluate(!$this->model->hasAny($conditions),
                "is already in use", $fieldName, $params);
    }

    /**
     * Checks if the length of the string value defined by the field name is within the range
     *      specified by $params['min'], $params['max'], or both.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if length of the value of the field name is within the specified range;
     *      false otherwise.
     */
    function validateLength($fieldName, $params)
    {
        $val = $this->data[$this->name][$fieldName];
        $length = strlen($val);

        if (array_key_exists('min', $params) && array_key_exists('max', $params))
        {
            return $this->_evaluate($length>= $params['min'] && $length <= $params['max'],
                    "should be between {$params['min']} and {$params['max']} characters long",
                    $fieldName, $params);
        }
        else if (array_key_exists('min', $params))
        {
            return $this->_evaluate($length>= $params['min'],
                    "should be at least {$params['min']} characters long",
                    $fieldName, $params);
        }
        else if (array_key_exists('max', $params))
        {
            return $this->_evaluate($length <= $params['max'],
                    "should be at most {$params['max']} characters long",
                    $fieldName, $params);
        }
    }

    /**
     * Checks if the numeric value defined by the field name is within the range
     *      specified by $params['min'], $params['max'], or both.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if numeric value of the field name is within the specified range;
     *      false otherwise.
     */
    function validateRange($fieldName, $params)
    {
        if ($result = $this->validateNumber($fieldName, $params))
        {
            return $result;
        }

        $val = $this->data[$this->name][$fieldName];

        if (array_key_exists('min', $params) && array_key_exists('max', $params))
        {
            return $this->_evaluate($val>= $params['min'] && $val <= $params['max'],
                    "should be between {$params['min']} and {$params['max']}",
                    $fieldName, $params);
        }
        else if (array_key_exists('min', $params))
        {
            return $this->_evaluate($val>= $params['min'],
                    "should be at least {$params['min']}",
                    $fieldName, $params);
        }
        else if (array_key_exists('max', $params))
        {
            return $this->_evaluate($val <= $params['max'],
                    "should be at most {$params['max']}",
                    $fieldName, $params);
        }
    }

    /**
     * Checks if the value defined by the field name corresponds with it's confirmation value,
     *      which is defined by the field specified in {@link $params}['confirm_var'] if defined,
     *      or by <the field name>_confirmation.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name corresponds to its confirmation
     *      value; false otherwise.
     */
    function validateConfirmed($fieldName, $params)
    {
        $val = $this->data[$this->name][$fieldName];

        if (array_key_exists('confirm_var', $params))
        {
            $confirmVar = $params['confirm_var'];               
        }
        else
        {
            if (empty($this->data[$this->name][$fieldName . '_confirmation']))
            {
                $returnValue = false;
            }
            $confirmVar = $fieldName . '_confirmation';
        }
       
        if (empty($this->data[$this->name][$confirmVar]))
        {
            $this->data[$this->name][$confirmVar] = null;
        }
       
        return $this->_evaluate($val == $this->data[$this->name][$confirmVar],
            "is not confirmed", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is a file.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is a file; false otherwise.
     */
    function validateFile($fieldName, $params)
    {
        $file = $this->data[$this->name][$fieldName];
       
        $returnValue = true;   

        if ($file['error'] == UPLOAD_ERR_OK)
        {
            if (isset($params['allowedTypes']) && !in_array($file['type'], $params['allowedTypes']))
            {
                $returnValue = false;
            }
        }
        else
        {
            unset($this->data[$this->name][$fieldName]);
        }
       
        return $this->_evaluate($returnValue, "is not a valid file",
                $fieldName, $params);       
    }

    /**
     * Checks if the value defined by the field name is an image file.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is an image file; false otherwise.
     * @see Validation::validateFile()
     */
    function validateImageFile($fieldName, $params)
    {
        $params['allowedTypes'] = array('image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png',
                'image/x-png', 'image/x-jg', 'image/gif');
               
        return $this->_evaluate($this->validateFile($fieldName, $params),
                "is not a valid image file", $fieldName, $params);       
    }
   
    /**
     * Checks if the value defined by the field name is a properly uploaded file.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is a properly uploaded file; false otherwise.
     */
    function validateUploaded($fieldName, $params)
    {
        return $this->_evaluate(is_uploaded_file($this->data[$this->name][$fieldName]['tmp_name']),
                "was not uploaded", $fieldName, $params);
    }

    /**
     * Checks if the value defined by the field name is a date set in the future.  This
     * automatically checks if the value is in proper date format.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is a future date; false otherwise.
     * @see Validation::validateDate()
     */
    function validateFutureDate($fieldName, $params)
    {       
        if ($result = $this->validateDate($fieldName, $params))
        {
            return $result;
        }
       
        $date = strtotime($this->data[$this->name][$fieldName]);       

        return $this->_evaluate($date> time(), "is not set in a future date", $fieldName, $params);
    }
   
    /**
     * Checks if the value defined by the field name is in proper date format (yyyy-mm-dd).
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is in proper date format; false otherwise.
     */   
    function validateDate($fieldName, $params)
    {
        $date = $this->data[$this->name][$fieldName];
       
        $datePattern = '/^\d{4}-\d?\d-\d?\d$/';
        if ($date && preg_match($datePattern, $date))
        {
            $date = explode('-',$date);
            $result = checkdate($date[1], $date[2], $date[0]);
        }
        else
        {
            $result = false;
        }
       
        return $this->_evaluate($result, "is not a valid date", $fieldName, $params);       
    }
   
    /**
     * Checks if the value defined by the field name is in proper datetime format
     * (yyyy-mm-dd HH:MM:SS).
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is in proper datetime format; false otherwise.
     */     
    function validateDatetime($fieldName, $params)
    {
        $dateTime = $this->data[$this->name][$fieldName];

        $dateTimePattern = '/^\d{4}-\d?\d-\d?\d '
                . '([01]?[0-9]|[2][0-4]):([0-5]?[0-9]):([0-5]?[0-9])$/';
       
        if ($dateTime && preg_match($dateTimePattern, $dateTime))
        {
            list($date, $time) = explode(' ', $dateTime);
            $date = explode('-',$date);
            $result = checkdate($date[1], $date[2], $date[0]);
        }
        else
        {
            $result = false;
        }
       
        return $this->_evaluate($result, "is not a valid datetime", $fieldName, $params);       
    }
   
    /**
     * Runs a method in the model object, passing to it the value of the specified field and the
     * additional parameters.  The method's name is checked from the value of $params['method']; if
     * this is not available, then this function will try to call validate{Fieldname} instead.  If
     * the method call fails, this function will return false.
     *
     * @param string $fieldName The name of the field to validate.
     * @param array $params Extra validation parameters.
     * @return bool True if value of the field name is in proper datetime format; false otherwise.
     */     
    function validateMethod($fieldName, $params)
    {
        $method = isset($params['method']) ? $params['method'] : 'validate'
                . Inflector::humanize($fieldName);
       
        if (!method_exists($this->model, $method))
        {
            $this->errorCount++;
            return false;
        }
        else
        {
            if (call_user_func(array(&$this->model, $method), $this->data[$this->name][$fieldName],
                    $params))
            {
                return true;
            }
            else
            {
                $this->errorCount++;
                return false;
            }
        }

    }

}

?>
